/**
 ******************************************************************************
 * @file           : bsp_rtc.c
 * @brief          : RTC Driver Implementation (STM32F4xx)
 ******************************************************************************
 * @attention
 *
 * RTC驱动实现文件
 * STM32F4xx日历型RTC，使用BCD编码
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/RTC/bsp_rtc.h"
#include "./BSP/RTC/bsp_rtc_config.h"
#include <time.h>

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/

/* BCD编码转换宏 */
#define BCD_TO_BYTE(bcd)    ((((bcd) >> 4) * 10) + ((bcd) & 0x0F))
#define BYTE_TO_BCD(byte)   ((((byte) / 10) << 4) | ((byte) % 10))

/* Private variables ---------------------------------------------------------*/
static RTC_HandleTypeDef sg_rtc_handle;
static volatile RTC_Error_e sg_rtc_last_error = RTC_OK;  /*!< RTC最后一次错误 */
static volatile RTC_State_e sg_rtc_state = RTC_STATE_IDLE;  /*!< RTC当前状态 */

/* 每月天数表（平年） */
static const uint8_t s_month_days[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* Private function prototypes -----------------------------------------------*/
static uint8_t rtc_is_valid_datetime(const RTC_DateTime_t *datetime);
static uint8_t rtc_get_month_days(uint16_t year, uint8_t month);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       验证日期时间有效性
 *
 * @param[in]   datetime 日期时间结构体
 *
 * @retval      0=有效，1=无效
 */
static uint8_t rtc_is_valid_datetime(const RTC_DateTime_t *datetime)
{
    if (datetime == NULL)
    {
        return 1;
    }

    /* 检查年份 */
    if (datetime->year < 2000 || datetime->year > 2099)
    {
        return 1;
    }

    /* 检查月份 */
    if (datetime->month < 1 || datetime->month > 12)
    {
        return 1;
    }

    /* 检查日期 */
    uint8_t days = rtc_get_month_days(datetime->year, datetime->month);
    if (datetime->day < 1 || datetime->day > days)
    {
        return 1;
    }

    /* 检查时间 */
    if (datetime->hour > 23 || datetime->minute > 59 || datetime->second > 59)
    {
        return 1;
    }

    return 0;
}

/**
 * @brief       获取月份天数
 *
 * @param[in]   year 年份
 * @param[in]   month 月份（1-12）
 *
 * @retval      天数
 */
static uint8_t rtc_get_month_days(uint16_t year, uint8_t month)
{
    if (month < 1 || month > 12)
    {
        return 0;
    }

    if (month == 2 && rtc_is_leap_year(year))
    {
        return 29;
    }

    return s_month_days[month - 1];
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       RTC初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置RTC使用LSE或LSI时钟源
 */
void rtc_init(void)
{
    /* 使能电源时钟 */
    __HAL_RCC_PWR_CLK_ENABLE();

    /* 允许访问RTC备份域 */
    HAL_PWR_EnableBkUpAccess();

#if (RTC_USE_LSE == 1)
    /* 配置LSE */
    RCC_OscInitTypeDef osc_init = {0};
    osc_init.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    osc_init.LSEState = RCC_LSE_ON;
    osc_init.PLL.PLLState = RCC_PLL_NONE;

    if (HAL_RCC_OscConfig(&osc_init) != HAL_OK)
    {
        log_e("LSE configuration failed");
        return;
    }

    /* 选择LSE作为RTC时钟源 */
    __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
#else
    /* 配置LSI */
    RCC_OscInitTypeDef osc_init = {0};
    osc_init.OscillatorType = RCC_OSCILLATORTYPE_LSI;
    osc_init.LSIState = RCC_LSI_ON;
    osc_init.PLL.PLLState = RCC_PLL_NONE;

    if (HAL_RCC_OscConfig(&osc_init) != HAL_OK)
    {
        log_e("LSI configuration failed");
        return;
    }

    /* 选择LSI作为RTC时钟源 */
    __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI);
#endif

    /* 使能RTC时钟 */
    __HAL_RCC_RTC_ENABLE();

    /* 配置RTC */
    sg_rtc_handle.Instance = RTC;
    sg_rtc_handle.Init.HourFormat = RTC_HOURFORMAT_24;
    sg_rtc_handle.Init.AsynchPrediv = RTC_ASYNC_PREDIV;
    sg_rtc_handle.Init.SynchPrediv = RTC_SYNC_PREDIV;
    sg_rtc_handle.Init.OutPut = RTC_OUTPUT_DISABLE;
    sg_rtc_handle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    sg_rtc_handle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;

    if (HAL_RTC_Init(&sg_rtc_handle) != HAL_OK)
    {
        log_e("RTC initialization failed");
        sg_rtc_state = RTC_STATE_ERROR;
        sg_rtc_last_error = RTC_ERROR_HAL;
        return;
    }

    sg_rtc_state = RTC_STATE_READY;
    sg_rtc_last_error = RTC_OK;
    log_i("RTC initialized: %s", RTC_USE_LSE ? "LSE" : "LSI");
}

/**
 * @brief       RTC去初始化
 *
 * @param       无
 *
 * @retval      无
 */
void rtc_deinit(void)
{
    HAL_RTC_DeInit(&sg_rtc_handle);
    log_i("RTC deinitialized");
}

/**
 * @brief       设置日期和时间
 *
 * @param[in]   datetime 日期时间结构体
 *
 * @retval      RTC_OK - 成功
 * @retval      RTC_ERROR_PARAM - 参数错误
 * @retval      RTC_ERROR_HAL - HAL库错误
 */
RTC_Error_e rtc_set_datetime(const RTC_DateTime_t *datetime)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    /* 验证日期时间 */
    if (rtc_is_valid_datetime(datetime) != 0)
    {
        log_e("Invalid datetime");
        sg_rtc_last_error = RTC_ERROR_PARAM;
        return RTC_ERROR_PARAM;
    }

    /* 配置时间 */
    time.Hours = datetime->hour;
    time.Minutes = datetime->minute;
    time.Seconds = datetime->second;
    time.TimeFormat = RTC_HOURFORMAT_24;
    time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    time.StoreOperation = RTC_STOREOPERATION_RESET;

    if (HAL_RTC_SetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("Set time failed");
        sg_rtc_last_error = RTC_ERROR_HAL;
        return RTC_ERROR_HAL;
    }

    /* 配置日期 */
    date.Year = datetime->year - 2000;  /* RTC存储的是相对2000年的年份 */
    date.Month = datetime->month;
    date.Date = datetime->day;
    date.WeekDay = datetime->week;

    if (HAL_RTC_SetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        log_e("Set date failed");
        sg_rtc_last_error = RTC_ERROR_HAL;
        return RTC_ERROR_HAL;
    }

    log_i("RTC set: %04d-%02d-%02d %02d:%02d:%02d", 
          datetime->year, datetime->month, datetime->day,
          datetime->hour, datetime->minute, datetime->second);

    sg_rtc_last_error = RTC_OK;
    return RTC_OK;
}

/**
 * @brief       设置时间
 *
 * @param[in]   hour 小时（0-23）
 * @param[in]   minute 分钟（0-59）
 * @param[in]   second 秒（0-59）
 *
 * @retval      RTC_OK - 成功
 * @retval      RTC_ERROR_PARAM - 参数错误
 * @retval      RTC_ERROR_HAL - HAL库错误
 */
RTC_Error_e rtc_set_time(uint8_t hour, uint8_t minute, uint8_t second)
{
    RTC_TimeTypeDef time = {0};

    if (hour > 23 || minute > 59 || second > 59)
    {
        sg_rtc_last_error = RTC_ERROR_PARAM;
        return RTC_ERROR_PARAM;
    }

    time.Hours = hour;
    time.Minutes = minute;
    time.Seconds = second;
    time.TimeFormat = RTC_HOURFORMAT_24;
    time.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
    time.StoreOperation = RTC_STOREOPERATION_RESET;

    if (HAL_RTC_SetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN) != HAL_OK)
    {
        sg_rtc_last_error = RTC_ERROR_HAL;
        return RTC_ERROR_HAL;
    }

    sg_rtc_last_error = RTC_OK;
    return RTC_OK;
}

/**
 * @brief       设置日期
 *
 * @param[in]   year 年份（2000-2099）
 * @param[in]   month 月份（1-12）
 * @param[in]   day 日期（1-31）
 *
 * @retval      RTC_OK - 成功
 * @retval      RTC_ERROR_PARAM - 参数错误
 * @retval      RTC_ERROR_HAL - HAL库错误
 */
RTC_Error_e rtc_set_date(uint16_t year, uint8_t month, uint8_t day)
{
    RTC_DateTypeDef date = {0};

    if (year < 2000 || year > 2099 || month < 1 || month > 12)
    {
        sg_rtc_last_error = RTC_ERROR_PARAM;
        return RTC_ERROR_PARAM;
    }

    uint8_t days = rtc_get_month_days(year, month);
    if (day < 1 || day > days)
    {
        sg_rtc_last_error = RTC_ERROR_PARAM;
        return RTC_ERROR_PARAM;
    }

    date.Year = year - 2000;
    date.Month = month;
    date.Date = day;
    date.WeekDay = rtc_get_week(year, month, day);

    if (HAL_RTC_SetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN) != HAL_OK)
    {
        sg_rtc_last_error = RTC_ERROR_HAL;
        return RTC_ERROR_HAL;
    }

    sg_rtc_last_error = RTC_OK;
    return RTC_OK;
}

/**
 * @brief       获取日期和时间
 *
 * @param[out]  datetime 日期时间结构体
 *
 * @retval      无
 */
void rtc_get_datetime(RTC_DateTime_t *datetime)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    if (datetime == NULL)
    {
        return;
    }

    /* 读取时间 */
    HAL_RTC_GetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN);

    /* 读取日期（必须在读取时间后） */
    HAL_RTC_GetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN);

    /* 填充结构体 */
    datetime->year = date.Year + 2000;
    datetime->month = date.Month;
    datetime->day = date.Date;
    datetime->hour = time.Hours;
    datetime->minute = time.Minutes;
    datetime->second = time.Seconds;
    datetime->week = date.WeekDay;
}

/**
 * @brief       获取时间
 *
 * @param[out]  hour 小时
 * @param[out]  minute 分钟
 * @param[out]  second 秒
 *
 * @retval      无
 */
void rtc_get_time(uint8_t *hour, uint8_t *minute, uint8_t *second)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    HAL_RTC_GetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN);

    if (hour != NULL)
    {
        *hour = time.Hours;
    }

    if (minute != NULL)
    {
        *minute = time.Minutes;
    }

    if (second != NULL)
    {
        *second = time.Seconds;
    }
}

/**
 * @brief       获取日期
 *
 * @param[out]  year 年份
 * @param[out]  month 月份
 * @param[out]  day 日期
 *
 * @retval      无
 */
void rtc_get_date(uint16_t *year, uint8_t *month, uint8_t *day)
{
    RTC_TimeTypeDef time = {0};
    RTC_DateTypeDef date = {0};

    HAL_RTC_GetTime(&sg_rtc_handle, &time, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&sg_rtc_handle, &date, RTC_FORMAT_BIN);

    if (year != NULL)
    {
        *year = date.Year + 2000;
    }

    if (month != NULL)
    {
        *month = date.Month;
    }

    if (day != NULL)
    {
        *day = date.Date;
    }
}

/**
 * @brief       获取Unix时间戳
 *
 * @param       无
 *
 * @retval      时间戳（秒）
 */
uint32_t rtc_get_timestamp(void)
{
    RTC_DateTime_t datetime;
    struct tm timeinfo = {0};

    rtc_get_datetime(&datetime);

    timeinfo.tm_year = datetime.year - 1900;
    timeinfo.tm_mon = datetime.month - 1;
    timeinfo.tm_mday = datetime.day;
    timeinfo.tm_hour = datetime.hour;
    timeinfo.tm_min = datetime.minute;
    timeinfo.tm_sec = datetime.second;

    return (uint32_t)mktime(&timeinfo);
}

/**
 * @brief       设置Unix时间戳
 *
 * @param[in]   timestamp 时间戳（秒）
 *
 * @retval      无
 */
void rtc_set_timestamp(uint32_t timestamp)
{
    struct tm *timeinfo;
    time_t rawtime = (time_t)timestamp;
    RTC_DateTime_t datetime;

    timeinfo = localtime(&rawtime);

    datetime.year = timeinfo->tm_year + 1900;
    datetime.month = timeinfo->tm_mon + 1;
    datetime.day = timeinfo->tm_mday;
    datetime.hour = timeinfo->tm_hour;
    datetime.minute = timeinfo->tm_min;
    datetime.second = timeinfo->tm_sec;
    datetime.week = rtc_get_week(datetime.year, datetime.month, datetime.day);

    rtc_set_datetime(&datetime);
}

/**
 * @brief       计算星期几
 *
 * @param[in]   year 年份
 * @param[in]   month 月份（1-12）
 * @param[in]   day 日期（1-31）
 *
 * @retval      星期（1-7，1=周一）
 *
 * @note        使用蔡勒公式
 */
uint8_t rtc_get_week(uint16_t year, uint8_t month, uint8_t day)
{
    uint16_t y = year;
    uint8_t m = month;
    uint8_t d = day;
    uint8_t week;

    /* 蔡勒公式：1月和2月看作上一年的13月和14月 */
    if (m == 1 || m == 2)
    {
        m += 12;
        y--;
    }

    /* 计算星期 */
    week = (d + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400 + 1) % 7;

    /* 转换：0=周日 → 7，1~6不变 */
    if (week == 0)
    {
        week = 7;
    }

    return week;
}

/**
 * @brief       判断是否闰年
 *
 * @param[in]   year 年份
 *
 * @retval      1=闰年，0=平年
 */
uint8_t rtc_is_leap_year(uint16_t year)
{
    if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
    {
        return 1;
    }

    return 0;
}

/**
 * @brief       获取RTC状态
 *
 * @param       无
 *
 * @retval      RTC状态
 */
RTC_State_e rtc_get_state(void)
{
    return sg_rtc_state;
}

/**
 * @brief       打印RTC状态（调试接口）
 *
 * @param       无
 *
 * @retval      无
 */
void rtc_print_status(void)
{
    const char *state_str[] = {
        "RTC_STATE_IDLE",
        "RTC_STATE_READY",
        "RTC_STATE_BUSY",
        "RTC_STATE_ERROR"
    };
    
    const char *error_str[] = {
        "RTC_OK",
        "RTC_ERROR_PARAM",
        "RTC_ERROR_HAL"
    };
    
    if (sg_rtc_state < 4)
    {
        log_i("RTC State: %s", state_str[sg_rtc_state]);
    }
    else
    {
        log_i("RTC State: UNKNOWN");
    }
    
    if (sg_rtc_last_error < 3)
    {
        log_i("RTC Last Error: %s", error_str[sg_rtc_last_error]);
    }
    else
    {
        log_i("RTC Last Error: UNKNOWN");
    }
}
